from PyQt5.QtCore import QObject, pyqtSignal, QTimer, QRunnable, pyqtSlot, QThreadPool
from PyQt5.QtSerialPort import QSerialPort, QSerialPortInfo
import time
import sys
import os

# 添加lib目录到系统路径
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib'))
from serial_op.serial_op import at_ser_identify, dl_ser_identify, serial_at_rw

class SerialPortWorker(QRunnable):
    class WorkerSignals(QObject):
        finished = pyqtSignal(str, bool, str)  # 端口名, 是否有效, 端口类型(AT/DL)

    def __init__(self, port_info, baud_rates=None):
        super().__init__()
        self.port_info = port_info
        self.signals = self.WorkerSignals()
        self.baud_rates = baud_rates or [115200, 9600, 57600, 38400]

    @pyqtSlot()
    def run(self):
        port_name = self.port_info.portName()
        # 使用serial_op中的方法检测端口类型
        result, port_type = self.check_port_type()
        self.signals.finished.emit(port_name, result, port_type)

    def check_port_type(self):
        """检测端口类型，使用serial_op中的方法"""
        port_name = self.port_info.portName()
        
        try:
            # 首先尝试AT口检测
            at_result = self.is_at_port(port_name)
            if at_result:
                return True, "AT"
                
            # 如果不是AT口，尝试DL口检测
            dl_result = self.is_dl_port(port_name)
            if dl_result:
                return True, "DL"
                
            # 都不是则返回无效
            return False, "UNKNOWN"
            
        except Exception as e:
            print(f"端口 {port_name} 检测异常: {str(e)}")
            return False, "ERROR"

    def is_at_port(self, port_name):
        """使用serial_op中的方法检测AT口"""
        try:
            # 使用简单的AT指令测试
            response = serial_at_rw(port_name, 115200, 0.5, ['AT'])
            # 检查是否有响应且包含OK
            if response and 'AT' in response and any('OK' in resp for resp in response.get('AT', [])):
                return True
                
            # 尝试其他波特率
            for baud_rate in [9600, 57600, 38400]:
                response = serial_at_rw(port_name, baud_rate, 0.3, ['AT'])
                if response and 'AT' in response and any('OK' in resp for resp in response.get('AT', [])):
                    return True
                    
            return False
        except Exception:
            return False

    def is_dl_port(self, port_name):
        """检测是否为下载口"""
        # 下载口通常不响应AT指令，但可能有特定标识
        # 这里简化处理，通过端口描述判断
        try:
            for port_info in QSerialPortInfo.availablePorts():
                if port_info.portName() == port_name:
                    # 检查端口描述是否包含DL相关字符
                    if "DL" in port_info.description():
                        return True
            return False
        except Exception:
            return False


class SerialPortDetector(QObject):
    portListUpdated = pyqtSignal(list)  # 信号：当有效端口列表更新时触发
    atPortsUpdated = pyqtSignal(list)   # 新增：AT口列表更新信号
    dlPortsUpdated = pyqtSignal(list)   # 新增：DL口列表更新信号

    def __init__(self):
        super().__init__()
        self.valid_ports = []
        self.at_ports = []    # 新增：AT口列表
        self.dl_ports = []    # 新增：DL口列表
        self.invalid_ports_cache = {}
        self.cache_expire = 300
        self.thread_pool = QThreadPool()
        self.pending_ports = set()  # 正在检测中的端口
        self.paused = False  # 添加暂停标志
        self._init_detector()
        
    def pause_detection(self):
        """暂停串口检测"""
        print("暂停串口检测，保持当前设备列表")
        self.paused = True
        # 暂停定时器
        if hasattr(self, 'periodic_timer') and self.periodic_timer.isActive():
            self.periodic_timer.stop()
            
    def resume_detection(self):
        """恢复串口检测"""
        print("恢复串口检测")
        self.paused = False
        # 重新启动定时器
        if hasattr(self, 'periodic_timer') and not self.periodic_timer.isActive():
            self.periodic_timer.start(5000)
        # 立即执行一次检测
        self.detectSerialPorts()
        
    def _init_detector(self):
        # 创建单次定时器立即触发首次检测
        self.init_timer = QTimer()
        self.init_timer.timeout.connect(self._first_detection)
        self.init_timer.setSingleShot(True)
        self.init_timer.start(0)

    def _first_detection(self):
        # 执行首次检测
        self.detectSerialPorts()
        # 初始化周期定时器
        self.periodic_timer = QTimer()
        self.periodic_timer.timeout.connect(self.detectSerialPorts)
        self.periodic_timer.start(5000)

    def detectSerialPorts(self):
        """并发检测有效串口（包括AT口和DL口）"""
        # 如果检测被暂停，直接返回
        if self.paused:
            return
            
        current_time = time.time()
        # 清理过期缓存
        expired = [
            p
            for p, t in self.invalid_ports_cache.items()
            if current_time - t > self.cache_expire
        ]
        for p in expired:
            del self.invalid_ports_cache[p]

        # 获取所有可用端口
        available_ports = QSerialPortInfo.availablePorts()
        available_port_names = [port.portName() for port in available_ports]
        
        # 检查是否有已经不存在的有效端口（被拔出的端口）
        removed_ports = [port for port in self.valid_ports if port not in available_port_names]
        if removed_ports:
            for port in removed_ports:
                self.valid_ports.remove(port)
                # 同时从AT口和DL口列表中移除
                if port in self.at_ports:
                    self.at_ports.remove(port)
                if port in self.dl_ports:
                    self.dl_ports.remove(port)
            print(f"检测到串口被移除: {removed_ports}")
            self.portListUpdated.emit(self.valid_ports)
            self.atPortsUpdated.emit(self.at_ports)
            self.dlPortsUpdated.emit(self.dl_ports)
        
        # 如果没有端口或所有端口都在检测中，则跳过本次检测
        if not available_ports or all(port_info.portName() in self.pending_ports for port_info in available_ports):
            return
            
        # 限制并发检测的端口数量，避免资源竞争
        max_concurrent = 3  # 最多同时检测3个端口
        current_pending = len(self.pending_ports)
        
        # 优先检测常用端口（如COM3等）
        def port_priority(port_info):
            port_name = port_info.portName()
            # 常用端口优先级高
            if port_name in ["COM3", "COM4", "COM5"]:
                return 0
            # 已知有效端口次之
            elif port_name in self.valid_ports:
                return 1
            # 其他端口优先级最低
            else:
                return 2
        
        # 按优先级排序可用端口
        available_ports.sort(key=port_priority)
            
        # 并发检测端口，但限制并发数量
        for port_info in available_ports:
            port_name = port_info.portName()
            # 跳过在缓存期内的无效端口和正在检测的端口
            if port_name in self.invalid_ports_cache or port_name in self.pending_ports:
                continue
                
            # 检查是否达到最大并发数
            if current_pending >= max_concurrent:
                break
                
            # 创建工作线程并启动
            worker = SerialPortWorker(port_info)
            worker.signals.finished.connect(self.on_port_checked)
            self.pending_ports.add(port_name)
            self.thread_pool.start(worker)
            current_pending += 1

    def on_port_checked(self, port_name, is_valid, port_type):
        """处理单个端口检测结果，包含端口类型信息"""
        # 如果检测被暂停，不更新端口列表
        if self.paused:
            self.pending_ports.discard(port_name)
            return
            
        # 从待检测列表中移除
        self.pending_ports.discard(port_name)
        
        current_time = time.time()
        if is_valid:
            print(f"发现有效串口: {port_name}, 类型: {port_type}")
            # 更新有效端口列表
            if port_name not in self.valid_ports:
                self.valid_ports.append(port_name)
                
                # 根据端口类型更新对应列表
                if port_type == "AT" and port_name not in self.at_ports:
                    self.at_ports.append(port_name)
                    self.atPortsUpdated.emit(self.at_ports)
                elif port_type == "DL" and port_name not in self.dl_ports:
                    self.dl_ports.append(port_name)
                    self.dlPortsUpdated.emit(self.dl_ports)
                    
                self.portListUpdated.emit(self.valid_ports)
        else:
            # 更新无效端口缓存
            self.invalid_ports_cache[port_name] = current_time
            # 如果之前是有效端口，现在变为无效，需要更新列表
            if port_name in self.valid_ports:
                self.valid_ports.remove(port_name)
                
                # 同时从AT口和DL口列表中移除
                if port_name in self.at_ports:
                    self.at_ports.remove(port_name)
                    self.atPortsUpdated.emit(self.at_ports)
                if port_name in self.dl_ports:
                    self.dl_ports.remove(port_name)
                    self.dlPortsUpdated.emit(self.dl_ports)
                    
                self.portListUpdated.emit(self.valid_ports)

    # 新增方法：直接使用serial_op中的方法获取AT口
    def get_at_ports(self):
        """直接使用serial_op中的方法获取AT口列表"""
        try:
            return at_ser_identify()
        except Exception as e:
            print(f"获取AT口列表失败: {str(e)}")
            return []
            
    # 新增方法：直接使用serial_op中的方法获取DL口
    def get_dl_ports(self):
        """直接使用serial_op中的方法获取DL口列表"""
        try:
            return dl_ser_identify()
        except Exception as e:
            print(f"获取DL口列表失败: {str(e)}")
            return []